package com.wqnmdb.im.netty.dispose.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wqnmdb.im.domain.contants.ConstantEnum;
import com.wqnmdb.im.domain.mongo.ImAffirm;
import com.wqnmdb.im.domain.mongo.ImUser;
import com.wqnmdb.im.domain.mongo.ImMessage;
import com.wqnmdb.im.netty.data.NettyData;
import com.wqnmdb.im.domain.netty.protobuf.Message;
import com.wqnmdb.im.domain.netty.protobuf.NettyModel;
import com.wqnmdb.im.service.mongo.MongoService;
import com.wqnmdb.im.service.redis.RedisStringService;
import com.wqnmdb.im.utils.DateUtil;
import com.wqnmdb.im.utils.PBEUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.aggregation.ArrayOperators;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 实时数据和检查服务
 */
@Slf4j
@Service
public class DataAndCheckService extends NettyData {

    @Autowired
    private MongoService mongoService;

    @Autowired
    private AsynAndCacheService asynAndCacheService;

    /**
     * 检查用户及应用是否存在
     */
    public ImUser checkUserAuth(String accessKey, String secretKey, String appName) {
        ImUser userInfo = null;
        if (asynAndCacheService.getApp(appName) == null) {
            return userInfo;
        }
        userInfo = mongoService.findOne(new Query(Criteria.where("auth").is(accessKey)
                .and("salt").is(secretKey).and("status").is(1)), ImUser.class, asynAndCacheService.getUserCollectionName(appName));
        if (userInfo == null) {
            return userInfo;
        }
        //校验
        String userId = PBEUtil.encryptAndDecrypt(accessKey, userInfo.getPassword(), secretKey, 2);
        if (!userInfo.getUserId().equals(Integer.parseInt(userId))) {
            return null;
        }
        log.info("用户信息：{}.{}.{}.{}.", appName, accessKey, secretKey, userInfo == null ? "暂无" : JSON.toJSONString(userInfo));
        return userInfo;
    }

    /**
     * 检查是否是消息的接收方及msg是否是未读状态
     * 1-确认消息检查  2-撤回消息检查
     */
    public ImMessage checkMsg(Integer userId, String msgUuId, String appName, String sessionId, int cmd) {
        ImUser user = asynAndCacheService.getUser(userId, asynAndCacheService.getUserCollectionName(appName));
        if (user != null){
            ImMessage message = mongoService.findOne(
                    new Query(Criteria.where("appName").is(appName).and("uuid").is(msgUuId).and("sessionId").is(sessionId)),
                    ImMessage.class, asynAndCacheService.getMsgCollectionName(appName));
            if (cmd == 1){
                //消息确认，必须是接收方才可以确认
                if (message != null && message.getToUserId().equals(user.getUserId())){
                    return message;
                }
            }else{
                //消息撤回检查，两种身份都可以操作
                if (message != null){
                    if (message.getToUserId().equals(user.getUserId())
                            || message.getFromUserId().equals(user.getUserId())){
                        return message;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 检查用户是否绑定
     */
    public boolean checkUserBinding(String appName, Integer userId) {
        ConcurrentHashMap<String, ChannelId> userChannel = APP_BINDING_CHANNEL.get(appName);
        return userChannel.containsKey(userId.toString());
    }

    /**
     * 获取未收到的确认消息
     */
    public List<ImAffirm> getMsgAffirm(Integer userId, String appName){
        Query query = new Query(Criteria.where("receiveUserId").is(userId).and("appName").is(appName))
                .with(Sort.by(Sort.Direction.ASC, "createTime"));
        List<ImAffirm> imAffirms = mongoService.find(query, ImAffirm.class, asynAndCacheService.getAffirmCollectionName(appName));
        return imAffirms;
    }

    /**
     * 获取未收到的消息
     */
    public List<ImMessage> getNotReceiveMsg(Integer userId, String appName){
        //获取10秒内未确认的消息
        Query query = new Query(Criteria.where("toUserId").is(userId).and("createTime").lte(DateUtil.getNowInt() - 10)
                .and("appName").is(appName).and("status").is(0)).with(Sort.by(Sort.Direction.ASC, "createTime"));
        List<ImMessage> imMsgs = mongoService.find(query, ImMessage.class, asynAndCacheService.getMsgCollectionName(appName));
        return imMsgs;
    }

    /**
     * 获取用户管道ID
     */
    public ChannelHandlerContext getUserChannel(Integer userId, String appName) {
        ConcurrentHashMap<ChannelId, ChannelHandlerContext> contextChannel = SYS_CONTEXT_CHANNEL.get(appName);
        ConcurrentHashMap<String, ChannelId> userChannel = APP_BINDING_CHANNEL.get(appName);
        ChannelId channelId = userChannel.get(userId.toString());
        return contextChannel.get(channelId);
    }

    /**
     * 创建拓展参数
     */
    public String getExtras(String uuid, int status, String sessionId){
        JSONObject extras = new JSONObject();
        if (StringUtils.isNotBlank(uuid)){
            extras.put("uuid", uuid);
        }
        if (StringUtils.isNotBlank(sessionId)){
            extras.put("sessionId", sessionId);
        }
        if (status != 0){
            extras.put("status", status);
        }
        return extras.toJSONString();
    }

    /**
     * 获取响应消息builder
     */
    public NettyModel.RespModel getRespModel(int type, int code, String extras, Message.MessageInfo.Builder msgInfo){
        NettyModel.RespModel.Builder respModel = NettyModel.RespModel.newBuilder();
        if (type > 0){
            respModel.setType(type);
        }
        if (code > 0){
            respModel.setCode(code);
        }
        if (StringUtils.isNotBlank(extras)){
            respModel.setExtras(extras);
        }
        if (msgInfo != null){
            respModel.setMsgInfo(msgInfo);
        }
        respModel.setTimestamp(DateUtil.getNowInt());
        return respModel.build();
    }
}


